package com.huang.exception;

import com.huang.domain.LoginUser;
import com.huang.domain.ResultObject;
import com.huang.enums.ResultStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.nio.file.AccessDeniedException;

// 全局异常处理
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /* ----------- 请求方法有误 ------------ */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResultObject<Boolean> methodNotAllowedExceptionHandler(HttpRequestMethodNotSupportedException ex) {
        log.info("[405 METHOD NOT ALLOWED] getUserName: {}", getUserName(), ex);
        return ResultObject.fail(ResultStatus.SYS_METHOD_NOT_ALLOWED);
    }

    /* ----------- 参数格式有误 ------------ */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResultObject<Boolean> messageNotReadableExceptionHandler(HttpMessageNotReadableException ex) {
        log.info("[405 METHOD NOT ALLOWED] getUserName: {}", getUserName(), ex);
        return ResultObject.fail(ResultStatus.SYS_PARAMETER_NOT_VALID);
    }

    /* ----------- 参数类型错误 ------------ */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResultObject<Boolean> methodArgumentTypeMismatchHandler(MethodArgumentTypeMismatchException ex) {
        log.info("[405 METHOD NOT ALLOWED] getUserName: {}", getUserName(), ex);
        return ResultObject.fail(ResultStatus.SYS_PARAMETER_NOT_VALID);
    }

    /* ----------- 缺少必要参数 ------------ */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResultObject<Boolean> missingServletRequestParameterException(MissingServletRequestParameterException ex) {
        log.info("[405 METHOD NOT ALLOWED] getUserName: {}", getUserName(), ex);
        return ResultObject.fail(ResultStatus.SYS_PARAMETER_REQUIRED);
    }

    /* ----------- 参数校验不通过 ------------ */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultObject<Boolean> bizExceptionHandler(MethodArgumentNotValidException ex) {
        log.info("[405 METHOD NOT ALLOWED] getUserName: {}", getUserName(), ex);
        return ResultObject.fail(ResultStatus.SYS_PARAMETER_NOT_VALID);
    }

    /* ----------- 参数校验不通过 ------------ */
    @ExceptionHandler(BindException.class)
    public ResultObject<Boolean> bindHandler(BindException ex) {
        log.info("[405 METHOD NOT ALLOWED] getUserName: {}", getUserName(), ex);
        return ResultObject.fail(ResultStatus.SYS_PARAMETER_NOT_VALID);
    }

    /* ----------- 认证失败 ------------ */
//    @ExceptionHandler(BadCredentialsException.class)
//    public ResultObject<Boolean> accessDeniedExceptionHandler(BadCredentialsException ex) {
//        log.info("[BAD CREDENTIALS EXCEPTION]", ex);
//        return ResultObject.fail(ResultStatus.AUTH_BAD_CREDENTIALS);
//    }

    /* ----------- 权限不足 ------------ */
    @ExceptionHandler(AccessDeniedException.class)
    public ResultObject<Boolean> accessDeniedExceptionHandler(AccessDeniedException ex) {
        log.info("[405 METHOD NOT ALLOWED] getUserName: {}", getUserName(), ex);
        return ResultObject.fail(ResultStatus.AUTH_FORBIDDEN);
    }

    /* ----------- 账号或密码错误 ------------ */
    @ExceptionHandler({BadCredentialsException.class, UsernameNotFoundException.class})
    public ResultObject<Boolean> serviceExceptionHandler(Exception ex) {
        log.info("[405 METHOD NOT ALLOWED] getUserName: {}", getUserName(), ex);
        return ResultObject.fail(ResultStatus.LOGIN_ERROR);
    }

    /* ----------- 缺省错误类型 F------------ */
    @ExceptionHandler(Exception.class)
    public ResultObject<Boolean> defaultExceptionHandler(Exception ex) {
        log.info("[405 METHOD NOT ALLOWED] getUserName: {}", getUserName(), ex);

        return ResultObject.fail(ResultStatus.SYS_UNKNOWN_ERROR);
    }

    private String getUserName() {
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (principal instanceof LoginUser) {
            LoginUser loginUser = (LoginUser) principal;
            return loginUser.getUsername();
        }
        return null;
    }
}
